/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// Qualcomm "PureVoice" (aka. "QCELP") Audio RTP Sources
// Implementation

#include "include/QCELPAudioRTPSource.hh"
#include "include/MultiFramedRTPSource.hh"
#include "include/FramedFilter.hh"
#include <string.h>
#include <stdlib.h>

// This source is implemented internally by two separate sources:
// (i) a RTP source for the raw (interleaved) QCELP frames, and
// (ii) a deinterleaving filter that reads from this.
// Define these two new classes here:

class RawQCELPRTPSource : public MultiFramedRTPSource {
public:
    static RawQCELPRTPSource *createNew(UsageEnvironment &env,
                                        Groupsock *RTPgs,
                                        unsigned char rtpPayloadFormat,
                                        unsigned rtpTimestampFrequency);

    unsigned char interleaveL() const { return fInterleaveL; }

    unsigned char interleaveN() const { return fInterleaveN; }

    unsigned char &frameIndex() { return fFrameIndex; } // index within pkt

private:
    RawQCELPRTPSource(UsageEnvironment &env, Groupsock *RTPgs,
                      unsigned char rtpPayloadFormat,
                      unsigned rtpTimestampFrequency);
    // called only by createNew()

    virtual ~RawQCELPRTPSource();

private:
    // redefined virtual functions:
    virtual Boolean processSpecialHeader(BufferedPacket *packet,
                                         unsigned &resultSpecialHeaderSize);

    virtual char const *MIMEtype() const;

    virtual Boolean hasBeenSynchronizedUsingRTCP();

private:
    unsigned char fInterleaveL, fInterleaveN, fFrameIndex;
    unsigned fNumSuccessiveSyncedPackets;
};

class QCELPDeinterleaver : public FramedFilter {
public:
    static QCELPDeinterleaver *createNew(UsageEnvironment &env,
                                         RawQCELPRTPSource *inputSource);

private:
    QCELPDeinterleaver(UsageEnvironment &env,
                       RawQCELPRTPSource *inputSource);
    // called only by "createNew()"

    virtual ~QCELPDeinterleaver();

    static void afterGettingFrame(void *clientData, unsigned frameSize,
                                  unsigned numTruncatedBytes,
                                  struct timeval presentationTime,
                                  unsigned durationInMicroseconds);

    void afterGettingFrame1(unsigned frameSize, struct timeval presentationTime);

private:
    // Redefined virtual functions:
    void doGetNextFrame();

    virtual void doStopGettingFrames();

private:
    class QCELPDeinterleavingBuffer *fDeinterleavingBuffer;

    Boolean fNeedAFrame;
};


////////// QCELPAudioRTPSource implementation //////////

FramedSource *
QCELPAudioRTPSource::createNew(UsageEnvironment &env,
                               Groupsock *RTPgs,
                               RTPSource *&resultRTPSource,
                               unsigned char rtpPayloadFormat,
                               unsigned rtpTimestampFrequency) {
    RawQCELPRTPSource *rawRTPSource;
    resultRTPSource = rawRTPSource
            = RawQCELPRTPSource::createNew(env, RTPgs, rtpPayloadFormat,
                                           rtpTimestampFrequency);
    if (resultRTPSource == NULL) return NULL;

    QCELPDeinterleaver *deinterleaver
            = QCELPDeinterleaver::createNew(env, rawRTPSource);
    if (deinterleaver == NULL) {
        Medium::close(resultRTPSource);
        resultRTPSource = NULL;
    }

    return deinterleaver;
}


////////// QCELPBufferedPacket and QCELPBufferedPacketFactory //////////

// A subclass of BufferedPacket, used to separate out QCELP frames.

class QCELPBufferedPacket : public BufferedPacket {
public:
    QCELPBufferedPacket(RawQCELPRTPSource &ourSource);

    virtual ~QCELPBufferedPacket();

private: // redefined virtual functions
    virtual unsigned nextEnclosedFrameSize(unsigned char *&framePtr,
                                           unsigned dataSize);

private:
    RawQCELPRTPSource &fOurSource;
};

class QCELPBufferedPacketFactory : public BufferedPacketFactory {
private: // redefined virtual functions
    virtual BufferedPacket *createNewPacket(MultiFramedRTPSource *ourSource);
};


///////// RawQCELPRTPSource implementation ////////

RawQCELPRTPSource *
RawQCELPRTPSource::createNew(UsageEnvironment &env, Groupsock *RTPgs,
                             unsigned char rtpPayloadFormat,
                             unsigned rtpTimestampFrequency) {
    return new RawQCELPRTPSource(env, RTPgs, rtpPayloadFormat,
                                 rtpTimestampFrequency);
}

RawQCELPRTPSource::RawQCELPRTPSource(UsageEnvironment &env,
                                     Groupsock *RTPgs,
                                     unsigned char rtpPayloadFormat,
                                     unsigned rtpTimestampFrequency)
        : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat,
                               rtpTimestampFrequency,
                               new QCELPBufferedPacketFactory),
          fInterleaveL(0), fInterleaveN(0), fFrameIndex(0),
          fNumSuccessiveSyncedPackets(0) {
}

RawQCELPRTPSource::~RawQCELPRTPSource() {
}

Boolean RawQCELPRTPSource
::processSpecialHeader(BufferedPacket *packet,
                       unsigned &resultSpecialHeaderSize) {
    unsigned char *headerStart = packet->data();
    unsigned packetSize = packet->dataSize();

    // First, check whether this packet's RTP timestamp is synchronized:
    if (RTPSource::hasBeenSynchronizedUsingRTCP()) {
        ++fNumSuccessiveSyncedPackets;
    } else {
        fNumSuccessiveSyncedPackets = 0;
    }

    // There's a 1-byte header indicating the interleave parameters
    if (packetSize < 1) return False;

    // Get the interleaving parameters from the 1-byte header,
    // and check them for validity:
    unsigned char const firstByte = headerStart[0];
    unsigned char const interleaveL = (firstByte & 0x38) >> 3;
    unsigned char const interleaveN = firstByte & 0x07;
#ifdef DEBUG
    fprintf(stderr, "packetSize: %d, interleaveL: %d, interleaveN: %d\n", packetSize, interleaveL, interleaveN);
#endif
    if (interleaveL > 5 || interleaveN > interleaveL) return False; //invalid

    fInterleaveL = interleaveL;
    fInterleaveN = interleaveN;
    fFrameIndex = 0; // initially

    resultSpecialHeaderSize = 1;
    return True;
}

char const *RawQCELPRTPSource::MIMEtype() const {
    return "audio/QCELP";
}

Boolean RawQCELPRTPSource::hasBeenSynchronizedUsingRTCP() {
    // Don't report ourselves as being synchronized until we've received
    // at least a complete interleave cycle of synchronized packets.
    // This ensures that the receiver is currently getting a frame from
    // a packet that was synchronized.
    if (fNumSuccessiveSyncedPackets > (unsigned) (fInterleaveL + 1)) {
        fNumSuccessiveSyncedPackets = fInterleaveL + 2; // prevents overflow
        return True;
    }
    return False;
}


///// QCELPBufferedPacket and QCELPBufferedPacketFactory implementation

QCELPBufferedPacket::QCELPBufferedPacket(RawQCELPRTPSource &ourSource)
        : fOurSource(ourSource) {
}

QCELPBufferedPacket::~QCELPBufferedPacket() {
}

unsigned QCELPBufferedPacket::
nextEnclosedFrameSize(unsigned char *&framePtr, unsigned dataSize) {
    // The size of the QCELP frame is determined by the first byte:
    if (dataSize == 0) return 0; // sanity check
    unsigned char const firstByte = framePtr[0];

    unsigned frameSize;
    switch (firstByte) {
        case 0: {
            frameSize = 1;
            break;
        }
        case 1: {
            frameSize = 4;
            break;
        }
        case 2: {
            frameSize = 8;
            break;
        }
        case 3: {
            frameSize = 17;
            break;
        }
        case 4: {
            frameSize = 35;
            break;
        }
        default: {
            frameSize = 0;
            break;
        }
    }

#ifdef DEBUG
    fprintf(stderr, "QCELPBufferedPacket::nextEnclosedFrameSize(): frameSize: %d, dataSize: %d\n", frameSize, dataSize);
#endif
    if (dataSize < frameSize) return 0;

    ++fOurSource.frameIndex();
    return frameSize;
}

BufferedPacket *QCELPBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource *ourSource) {
    return new QCELPBufferedPacket((RawQCELPRTPSource &) (*ourSource));
}

///////// QCELPDeinterleavingBuffer /////////
// (used to implement QCELPDeinterleaver)

#define QCELP_MAX_FRAME_SIZE 35
#define QCELP_MAX_INTERLEAVE_L 5
#define QCELP_MAX_FRAMES_PER_PACKET 10
#define QCELP_MAX_INTERLEAVE_GROUP_SIZE \
    ((QCELP_MAX_INTERLEAVE_L+1)*QCELP_MAX_FRAMES_PER_PACKET)

class QCELPDeinterleavingBuffer {
public:
    QCELPDeinterleavingBuffer();

    virtual ~QCELPDeinterleavingBuffer();

    void deliverIncomingFrame(unsigned frameSize,
                              unsigned char interleaveL,
                              unsigned char interleaveN,
                              unsigned char frameIndex,
                              unsigned short packetSeqNum,
                              struct timeval presentationTime);

    Boolean retrieveFrame(unsigned char *to, unsigned maxSize,
                          unsigned &resultFrameSize, unsigned &resultNumTruncatedBytes,
                          struct timeval &resultPresentationTime);

    unsigned char *inputBuffer() { return fInputBuffer; }

    unsigned inputBufferSize() const { return QCELP_MAX_FRAME_SIZE; }

private:
    class FrameDescriptor {
    public:
        FrameDescriptor();

        virtual ~FrameDescriptor();

        unsigned frameSize;
        unsigned char *frameData;
        struct timeval presentationTime;
    };

    // Use two banks of descriptors - one for incoming, one for outgoing
    FrameDescriptor fFrames[QCELP_MAX_INTERLEAVE_GROUP_SIZE][2];
    unsigned char fIncomingBankId; // toggles between 0 and 1
    unsigned char fIncomingBinMax; // in the incoming bank
    unsigned char fOutgoingBinMax; // in the outgoing bank
    unsigned char fNextOutgoingBin;
    Boolean fHaveSeenPackets;
    u_int16_t fLastPacketSeqNumForGroup;
    unsigned char *fInputBuffer;
    struct timeval fLastRetrievedPresentationTime;
};


////////// QCELPDeinterleaver implementation /////////

QCELPDeinterleaver *
QCELPDeinterleaver::createNew(UsageEnvironment &env,
                              RawQCELPRTPSource *inputSource) {
    return new QCELPDeinterleaver(env, inputSource);
}

QCELPDeinterleaver::QCELPDeinterleaver(UsageEnvironment &env,
                                       RawQCELPRTPSource *inputSource)
        : FramedFilter(env, inputSource),
          fNeedAFrame(False) {
    fDeinterleavingBuffer = new QCELPDeinterleavingBuffer();
}

QCELPDeinterleaver::~QCELPDeinterleaver() {
    delete fDeinterleavingBuffer;
}

static unsigned const uSecsPerFrame = 20000; // 20 ms

void QCELPDeinterleaver::doGetNextFrame() {
    // First, try getting a frame from the deinterleaving buffer:
    if (fDeinterleavingBuffer->retrieveFrame(fTo, fMaxSize,
                                             fFrameSize, fNumTruncatedBytes,
                                             fPresentationTime)) {
        // Success!
        fNeedAFrame = False;

        fDurationInMicroseconds = uSecsPerFrame;

        // Call our own 'after getting' function.  Because we're not a 'leaf'
        // source, we can call this directly, without risking
        // infinite recursion
        afterGetting(this);
        return;
    }

    // No luck, so ask our source for help:
    fNeedAFrame = True;
    if (!fInputSource->isCurrentlyAwaitingData()) {
        fInputSource->getNextFrame(fDeinterleavingBuffer->inputBuffer(),
                                   fDeinterleavingBuffer->inputBufferSize(),
                                   afterGettingFrame, this,
                                   FramedSource::handleClosure, this);
    }
}

void QCELPDeinterleaver::doStopGettingFrames() {
    fNeedAFrame = False;
    fInputSource->stopGettingFrames();
}

void QCELPDeinterleaver
::afterGettingFrame(void *clientData, unsigned frameSize,
                    unsigned /*numTruncatedBytes*/,
                    struct timeval presentationTime,
                    unsigned /*durationInMicroseconds*/) {
    QCELPDeinterleaver *deinterleaver = (QCELPDeinterleaver *) clientData;
    deinterleaver->afterGettingFrame1(frameSize, presentationTime);
}

void QCELPDeinterleaver
::afterGettingFrame1(unsigned frameSize, struct timeval presentationTime) {
    RawQCELPRTPSource *source = (RawQCELPRTPSource *) fInputSource;

    // First, put the frame into our deinterleaving buffer:
    fDeinterleavingBuffer
            ->deliverIncomingFrame(frameSize, source->interleaveL(),
                                   source->interleaveN(), source->frameIndex(),
                                   source->curPacketRTPSeqNum(),
                                   presentationTime);

    // Then, try delivering a frame to the client (if he wants one):
    if (fNeedAFrame) doGetNextFrame();
}


////////// QCELPDeinterleavingBuffer implementation /////////

QCELPDeinterleavingBuffer::QCELPDeinterleavingBuffer()
        : fIncomingBankId(0), fIncomingBinMax(0),
          fOutgoingBinMax(0), fNextOutgoingBin(0),
          fHaveSeenPackets(False) {
    fInputBuffer = new unsigned char[QCELP_MAX_FRAME_SIZE];
}

QCELPDeinterleavingBuffer::~QCELPDeinterleavingBuffer() {
    delete[] fInputBuffer;
}

void QCELPDeinterleavingBuffer
::deliverIncomingFrame(unsigned frameSize,
                       unsigned char interleaveL,
                       unsigned char interleaveN,
                       unsigned char frameIndex,
                       unsigned short packetSeqNum,
                       struct timeval presentationTime) {
    // First perform a sanity check on the parameters:
    // (This is overkill, as the source should have already done this.)
    if (frameSize > QCELP_MAX_FRAME_SIZE
        || interleaveL > QCELP_MAX_INTERLEAVE_L || interleaveN > interleaveL
        || frameIndex == 0 || frameIndex > QCELP_MAX_FRAMES_PER_PACKET) {
#ifdef DEBUG
        fprintf(stderr, "QCELPDeinterleavingBuffer::deliverIncomingFrame() param sanity check failed (%d,%d,%d,%d)\n", frameSize, interleaveL, interleaveN, frameIndex);
#endif
        return;
    }

    // The input "presentationTime" was that of the first frame in this
    // packet.  Update it for the current frame:
    unsigned uSecIncrement = (frameIndex - 1) * (interleaveL + 1) * uSecsPerFrame;
    presentationTime.tv_usec += uSecIncrement;
    presentationTime.tv_sec += presentationTime.tv_usec / 1000000;
    presentationTime.tv_usec = presentationTime.tv_usec % 1000000;

    // Next, check whether this packet is part of a new interleave group
    if (!fHaveSeenPackets
        || seqNumLT(fLastPacketSeqNumForGroup, packetSeqNum)) {
        // We've moved to a new interleave group
        fHaveSeenPackets = True;
        fLastPacketSeqNumForGroup = packetSeqNum + interleaveL - interleaveN;

        // Switch the incoming and outgoing banks:
        fIncomingBankId ^= 1;
        unsigned char tmp = fIncomingBinMax;
        fIncomingBinMax = fOutgoingBinMax;
        fOutgoingBinMax = tmp;
        fNextOutgoingBin = 0;
    }

    // Now move the incoming frame into the appropriate bin:
    unsigned const binNumber
            = interleaveN + (frameIndex - 1) * (interleaveL + 1);
    FrameDescriptor &inBin = fFrames[binNumber][fIncomingBankId];
    unsigned char *curBuffer = inBin.frameData;
    inBin.frameData = fInputBuffer;
    inBin.frameSize = frameSize;
    inBin.presentationTime = presentationTime;

    if (curBuffer == NULL) curBuffer = new unsigned char[QCELP_MAX_FRAME_SIZE];
    fInputBuffer = curBuffer;

    if (binNumber >= fIncomingBinMax) {
        fIncomingBinMax = binNumber + 1;
    }
}

Boolean QCELPDeinterleavingBuffer
::retrieveFrame(unsigned char *to, unsigned maxSize,
                unsigned &resultFrameSize, unsigned &resultNumTruncatedBytes,
                struct timeval &resultPresentationTime) {
    if (fNextOutgoingBin >= fOutgoingBinMax) return False; // none left

    FrameDescriptor &outBin = fFrames[fNextOutgoingBin][fIncomingBankId ^ 1];
    unsigned char *fromPtr;
    unsigned char fromSize = outBin.frameSize;
    outBin.frameSize = 0; // for the next time this bin is used

    // Check whether this frame is missing; if so, return an 'erasure' frame:
    unsigned char erasure = 14;
    if (fromSize == 0) {
        fromPtr = &erasure;
        fromSize = 1;

        // Compute this erasure frame's presentation time via extrapolation:
        resultPresentationTime = fLastRetrievedPresentationTime;
        resultPresentationTime.tv_usec += uSecsPerFrame;
        if (resultPresentationTime.tv_usec >= 1000000) {
            ++resultPresentationTime.tv_sec;
            resultPresentationTime.tv_usec -= 1000000;
        }
    } else {
        // Normal case - a frame exists:
        fromPtr = outBin.frameData;
        resultPresentationTime = outBin.presentationTime;
    }

    fLastRetrievedPresentationTime = resultPresentationTime;

    if (fromSize > maxSize) {
        resultNumTruncatedBytes = fromSize - maxSize;
        resultFrameSize = maxSize;
    } else {
        resultNumTruncatedBytes = 0;
        resultFrameSize = fromSize;
    }
    memmove(to, fromPtr, resultFrameSize);

    ++fNextOutgoingBin;
    return True;
}

QCELPDeinterleavingBuffer::FrameDescriptor::FrameDescriptor()
        : frameSize(0), frameData(NULL) {
}

QCELPDeinterleavingBuffer::FrameDescriptor::~FrameDescriptor() {
    delete[] frameData;
}
